package com.mall.web.control.app.pay;

import java.net.URLEncoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mall.biz.pay.PayBizImpl;
import com.mall.client.mall.MallService;
import com.mall.client.mallGroupBuy.MallGroupbBuyService;
import com.mall.client.trade.TradeService;
import com.mall.common.Consts.tradeOrderStatus;
import com.mall.common.util.bit.PropertiesConfigure;
import com.mall.common.util.io.IOUtil;
import com.mall.constant.PropertiesKey;
import com.mall.pojo.Mall;
import com.mall.pojo.MallGroupBuy;
import com.mall.pojo.Trade;
import com.mall.web.annotation.UserLoginMessage;
import com.mall.web.util.HttpUtil;
import com.tencent.WXPay;
import com.tencent.common.Configure;
import com.tencent.common.RandomStringGenerator;
import com.tencent.common.Signature;
import com.tencent.common.XMLParser;
import com.tencent.protocol.jspay.JspayRequest;
import com.tencent.protocol.pay_unifiedorder.UnifiedorderRequest;
import com.tencent.protocol.pay_unifiedorder.UnifiedorderResponse;


/**
 * 微信支付
 * @author lihaiyang
 *
 */
@Controller
@RequestMapping("/wxPay/")
public class WeiXinPayController implements InitializingBean {

	private static final Logger log = LoggerFactory.getLogger(WeiXinPayController.class);

	@Autowired
	private PayBizImpl payBizImpl;
	
	
	
	
	/**
	 * 鉴权成功后返回页面地址
	 */
	private static String AUTH_RETURN_URI;

	/**
	 * 支付完成异步通知接口地址
	 */
	private static String PAY_NOTIFY_URL;
	

	/**
	 * 鉴权请求地址模板
	 */
	private static final String REDIRECT_URL_TPL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_base&state=%s#wechat_redirect";

	/**
	 * 回调通知处理成功后返回给微信的消息
	 */
	private static final String SUCCESS_MSG = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";

	/**
	 * 获取access_token接口地址
	 */
	private static final String ACCESS_TOKEN_API_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";

	@Autowired
	private PropertiesConfigure propertiesConfigure;

	

	@Autowired
	private MallService mallService;
	@Autowired
	private MallGroupbBuyService mallGroupBuyService;


	@Autowired
	private TradeService tradeService;

   
	

	/**
	 * 授权请求
	 * 
	 * @param request
	 * @param response
	 * @param modelMap
	 * @return
	 * @throws Exception
	 */
	@RequestMapping("auth")
	@UserLoginMessage
	public String auth(@RequestParam(required = true) Long orderId, HttpServletRequest request,
			HttpServletResponse response, ModelMap modelMap) throws Exception {
		log.error("开始授权请求...");
		// 将订单ID先保存在session中，待授权完成后继续取出使用
		log.error("pay>sessionId:{}", request.getSession().getId());
		String state = String.format("%08d", orderId);//  这里需要加密
		String redirectUrl = String.format(REDIRECT_URL_TPL, Configure.getAppid(),
				URLEncoder.encode(AUTH_RETURN_URI, "utf-8"), state);
		log.error("redirectTo:{}", redirectUrl);
		return "redirect:" + redirectUrl;
	}
	

	/**
	 * 授权请求回调
	 * 
	 * @param request
	 * @param response
	 * @param modelMap
	 * @return
	 * @throws Exception
	 */
	@RequestMapping("auth_return")
	@UserLoginMessage
	public String auth_return(@RequestParam(required = true) String code, @RequestParam(required = true) String state,
			HttpServletRequest request, HttpServletResponse response, ModelMap modelMap) throws Exception {
		log.error("auth_return收到授权回调请求,code:{},state:{}...", code, state);
		log.error("auth_return>sessionId:{}", request.getSession().getId());
		Long orderId = Long.valueOf(state);
		log.error("开始获取用户authinfo...");
		// 获取用户openid
		String authResponse = HttpUtil.sendGet(ACCESS_TOKEN_API_URL,
				String.format("appid=%s&secret=%s&code=%s&grant_type=authorization_code", Configure.getAppid(),
						Configure.getAppSecret(), code));
		JSONObject authResponseJson = JSON.parseObject(authResponse);
		log.error("用户authInfo:" + authResponseJson.toString());
		Integer errcode = authResponseJson.getInteger("errcode");
		if (errcode != null && errcode != 0) {
			log.error("请求用户authInfo失败,errcode:{}", errcode);
			return null; // TODO 跳转到error页
		}
		String openid = authResponseJson.getString("openid");
		if (StringUtils.isBlank(openid)) {
			log.error("openid is null");
			return null; // TODO 跳转到error页
		}
		log.error("解析出openid:" + openid);
		Trade trade = this.tradeService.getTradeByOrderId(orderId);
		if (trade == null) {
			log.error("order is null");
			return null; // TODO 跳转到error页
		}

		String subject = "";
		if(trade.getMallId()!=null && trade.getMallId()>=0){
			if(trade.getType()==5){
				MallGroupBuy mallGroupBuy = this.mallGroupBuyService.getMallGroupBuyById(trade.getParentId());
				if (mallGroupBuy == null) {
					return null;
				}
				subject = "购买" + mallGroupBuy.getTitle();
			}else{
				Mall mall = this.mallService.getMallById(trade.getMallId());
				if (mall == null) {
					return null;
				}
				subject = "购买" + mall.getMallName();
			}
			
		}else{
			subject = "交易失败";
			return null;
		}
		// 下面开始发起预支付请求
		UnifiedorderRequest reqData = new UnifiedorderRequest();
		reqData.setBody(subject);
		reqData.setOut_trade_no(trade.getOutTradeNo());
		reqData.setTotal_fee(trade.getPrice() + "");
		reqData.setSpbill_create_ip(request.getRemoteAddr());
		reqData.setNotify_url(PAY_NOTIFY_URL);
		reqData.setTrade_type("JSAPI");
		reqData.setOpenid(openid);
		log.error("开始发送统一下单请求...");
		String prepayResponseXml = null;
		try {
			prepayResponseXml = WXPay.doUnifiedorder(reqData);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			return null;
		}
		log.info("prepayResponseXml:" + prepayResponseXml);
		if (StringUtils.isBlank(prepayResponseXml)) {
			log.error("prepay response is null");
			return null; // TODO 跳转到error页
		}

		// 下面准备进入到真正的支付页面
		UnifiedorderResponse unifiedorderResponse = new UnifiedorderResponse().wrapFromXml(prepayResponseXml);
		log.info("unifiedorderResponse:{}", unifiedorderResponse.toString());
		if (!"SUCCESS".equals(unifiedorderResponse.getReturn_code())
				|| !"SUCCESS".equals(unifiedorderResponse.getResult_code())) {
			log.error("pay failed");
			return null; // TODO 跳转到error页
		}

		return "redirect:"+ propertiesConfigure.getProperties(PropertiesKey.httpbeginurl)+"/wxPay/payView.htm?prepay_id=" + unifiedorderResponse.getPrepay_id() + "&orderId=" + orderId;
	}

	/**
	 * 发起微信支付自动跳转页面
	 * 
	 * @param prepay_id
	 * @param request
	 * @param response
	 * @param modelMap
	 */
	@RequestMapping("payView")
	@UserLoginMessage
	public String payView(@RequestParam(required = true) String prepay_id, @RequestParam(required = true) Long orderId,
			HttpServletRequest request, HttpServletResponse response, ModelMap modelMap) {

		log.info("开始获取access_token");
		String accessTokenResponseJsonStr = HttpUtil.sendGet("https://api.weixin.qq.com/cgi-bin/token", String.format(
				"grant_type=client_credential&appid=%s&secret=%s", Configure.getAppid(), Configure.getAppSecret()));
		log.info("获取access_token请求结果：{}", accessTokenResponseJsonStr);
		JSONObject accessTokenResponseJsonObj = JSON.parseObject(accessTokenResponseJsonStr);
		
		//access_token是公众号的全局唯一票据，公众号调用各接口时都需使用access_token。
		//正常情况下access_token有效期为7200秒，重复获取将导致上次获取的access_token失效。
		String access_token = accessTokenResponseJsonObj.getString("access_token");
		if (StringUtils.isBlank(access_token)) {
			log.error("获取access_token失败！");
			return null;
		}

		//生成签名之前必须先了解一下jsapi_ticket，jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下，jsapi_ticket的有效期为7200秒，通过access_token来获取。
		//由于获取jsapi_ticket的api调用次数非常有限，频繁刷新jsapi_ticket会导致api调用受限，影响自身业务，开发者必须在自己的服务全局缓存jsapi_ticket
		log.info("开始获取ticket");
		String ticketresponseJsonStr = HttpUtil.sendGet("https://api.weixin.qq.com/cgi-bin/ticket/getticket",
				String.format("access_token=%s&type=jsapi", access_token));
		log.info("获取ticket结果：{}", ticketresponseJsonStr);
		JSONObject ticketresponseJsonObj = JSON.parseObject(ticketresponseJsonStr);
		String ticket = ticketresponseJsonObj.getString("ticket");

		Map<String, Object> jsWxConfig = new HashMap<String, Object>();
		jsWxConfig.put("noncestr", RandomStringGenerator.getRandomStringByLength(32));
		jsWxConfig.put("jsapi_ticket", ticket);
		jsWxConfig.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
		jsWxConfig.put("url",  propertiesConfigure.getProperties(PropertiesKey.httpbeginurl)
				+ "/wxPay/payView.htm?prepay_id=" + "&orderId=" + orderId);
		String configSign = Signature.getSignForJsConfig(jsWxConfig);
		jsWxConfig.put("appId", Configure.getAppid());
		jsWxConfig.put("sign", configSign);

		JspayRequest jspayRequest = new JspayRequest();
		jspayRequest.setTimeStamp(String.valueOf(System.currentTimeMillis() / 1000));
		jspayRequest.setNonceStr(RandomStringGenerator.getRandomStringByLength(32));
		jspayRequest.setExtPackage("prepay_id=" + prepay_id);
		jspayRequest.setAppId(Configure.getAppid());
		jspayRequest.setSignType("MD5");
		String paySign = Signature.getSign(jspayRequest.toMap());
		jspayRequest.setPaySign(paySign);

		modelMap.put("jspayRequest", jspayRequest);
		modelMap.put("jsWxConfig", jsWxConfig);
		modelMap.put("orderId", orderId);
		return "pay/wxpay";
	}

	/**
	 * 支付成功回调接口地址
	 * 
	 * @param request
	 * @param response
	 * @param modelMap
	 * @throws Exception
	 */
	private static final String SUCCESS_FLAG = "SUCCESS";
	private static final String OUR_ORDER_KEY = "out_trade_no";

	@RequestMapping("pay_notify")
	public void pay_notify(HttpServletRequest request, HttpServletResponse response, ModelMap modelMap)
			throws Exception {
		log.info("weixin pay_notify 收到回调。。。");
		String requestBody = IOUtil.readStringFromStream(request.getInputStream(), IOUtil.DEFAULT_CHARSET);
		Map<String, Object> requestParamsMap = XMLParser.getMapFromXML(requestBody);
		log.info(requestParamsMap.toString());
		do {
			if (!SUCCESS_FLAG.equals(MapUtils.getString(requestParamsMap, "return_code", null))
					|| !SUCCESS_FLAG.equals(MapUtils.getString(requestParamsMap, "result_code", null))) {
				break;
			}
			String ourOrderNO = MapUtils.getString(requestParamsMap, OUR_ORDER_KEY, null);
			if (StringUtils.isBlank(ourOrderNO)) {
				log.error("没有获取到out_trade_no");
				break;
			}
			Date currentTime = new Date();
			Trade trade = tradeService.getByOrderNO(ourOrderNO);
			if (trade == null) {
				log.error("没查询到充值订单[{}]", ourOrderNO);
				break;
			}
			if (trade.getStatus() != tradeOrderStatus.STATUS_NOTPAY) {
				log.error("订单状态不是未支付的，订单当前状态未[{}]", trade.getStatus());
				break;
			}
//			Trade updateTrade = new Trade();
			trade.setStatus(tradeOrderStatus.STATUS_PAYSUCCESS);
			trade.setGmtModify(currentTime);
			trade.setTradeNo(ourOrderNO);
			boolean success = tradeService.updateById(trade.getId(), trade);
			

			
			

			if (success) {
				log.info("更新订单状态成功");
				payBizImpl.paySuccessCallback(trade.getId());
				log.info("notify_url更新订单状态成功ok");
			} else {
				log.error("更新订单状态失败");
			}
		} while (false);
		// 通知微信处理成功
		response.getWriter().write(SUCCESS_MSG);
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		AUTH_RETURN_URI =  propertiesConfigure.getProperties(PropertiesKey.httpbeginurl) + "/wxPay/auth_return.htm";
		PAY_NOTIFY_URL =  propertiesConfigure.getProperties(PropertiesKey.httpbeginurl) + "/wxPay/pay_notify.htm";
	}

}